Dangling pointers and wild pointers in computer programming are data pointer that do not point to a valid object of the appropriate type. These are special cases of memory safety violations. More generally, dangling references and wild references are references that do not resolve to a valid destination.
Dangling pointers arise during object destruction, when an object that is pointed to by a given pointer is deleted or deallocated, without modifying the value of that said pointer, so that the pointer still points to the memory location of the deallocated memory. The system may reallocate the previously freed memory, and if the program then dereferences the (now) dangling pointer, unpredictable behavior may result, as the memory may now contain completely different data. If the program writes to memory referenced by a dangling pointer, a silent corruption of unrelated data may result, leading to subtle software bug that can be extremely difficult to find. If the memory has been reallocated to another process, then attempting to dereference the dangling pointer can cause segmentation faults (UNIX, Linux) or general protection faults (Windows). If the program has sufficient privileges to allow it to overwrite the bookkeeping data used by the kernel's memory allocator, the corruption can cause system instabilities. In object-oriented languages with garbage collection, dangling references are prevented by only destroying objects that are unreachable, meaning they do not have any incoming pointers; this is ensured either by tracing or reference counting. However, a finalizer may create new references to an object, requiring object resurrection to prevent a dangling reference.
Wild pointers, also called uninitialized pointers, arise when a pointer is used prior to initialization to some known state, which is possible in some programming languages. They show the same erratic behavior as dangling pointers, though they are less likely to stay undetected because many compilers will raise a warning at compile time if declared variables are accessed before being initialized.
A straightforward example is shown below:
char* dp = NULL;
// ...
{
char c;
dp = &c;
}
// c falls out of scope
// dp is now a dangling pointer
}
Another frequent source of dangling pointers is a jumbled combination of malloc() and free() library calls: a pointer becomes dangling when the block of memory it points to is freed. As with the previous example one way to avoid this is to make sure to reset the pointer to null after freeing its reference—as demonstrated below.
void func() {
char* dp = (char*)malloc(sizeof(char) * 10);
// ...
free(dp); // dp now becomes a dangling pointer
dp = NULL; // dp is no longer dangling
// ...
}
int num = 1234;
// ...
return #
}
(1945–1996) has created a complete object management system which is free of dangling reference phenomenon.Gianna Cioni, Antoni Kreczmar, ''Programmed deallocation without dangling reference'', Information Processing Letters, v. 18, '''1984''', pp. 179–185 A similar approach was proposed by Fisher and LeBlancC. N. Fisher, R. J. Leblanc, ''The implementation of run-time diagnostics in Pascal '', IEEE Transactions on Software Engineering, 6(4):313–319, 1980. under the name ''Locks-and-keys''.
This most often occurs due to jumping over the initialization, not by omitting it. Most compilers are able to warn about this.
char* dp; // dp is a wild pointer
static char* scp; /* scp is not a wild pointer:
* static variables are initialized to 0
* at start and retain their values from
* the last call afterwards.
* Using this feature may be considered bad
* style if not commented */
}
// Safe version of free()
static void safeFree(void** pp) {
int f(int i) {
// in debug mode, abort if pp is NULL
assert(pp);
// free(NULL) works properly, so no check is required besides the assert in debug mode
free(*pp); // deallocate chunk, note that free(NULL) is valid
*pp = NULL; // reset original pointer
}
char* p = NULL;
char* p2;
p = (char*)malloc(1000); // get a chunk
p2 = p; // copy the pointer
// use the chunk here
safeFree((void**)&p); // safety freeing; does not affect p2 variable
safeFree((void**)&p); // this second call won't fail as p is reset to NULL
char c = *p2; // p2 is still a dangling pointer, so this is undefined behavior.
return i + c;
}
The alternative version can be used even to guarantee the validity of an empty pointer before calling malloc():
These uses can be masked through #define directives to construct useful macros (a common one being #define XFREE(ptr) safeFree((void**)&(ptr))), creating something like a metalanguage or can be embedded into a tool library apart. In every case, programmers using this technique should use the safe versions in every instance where free() would be used; failing in doing so leads again to the problem. Also, this solution is limited to the scope of a single program or project, and should be properly documented.
Among more structured solutions, a popular technique to avoid dangling pointers in C++ is to use . A smart pointer typically uses reference counting to reclaim objects. Some other techniques include the tombstones method and the locks-and-keys method.
Another approach is to use the Boehm garbage collector, a conservative garbage collector that replaces standard memory allocation functions in C and C++ with a garbage collector. This approach completely eliminates dangling pointer errors by disabling frees, and reclaiming objects by garbage collection.
Another approach is to use a system such as CHERI, which stores pointers with additional metadata which may prevent invalid accesses by including lifetime information in pointers. CHERI typically requires support in the CPU to conduct these additional checks.
In languages like Java, dangling pointers cannot occur because there is no mechanism to explicitly deallocate memory. Rather, the garbage collector may deallocate memory, but only when the object is no longer reachable from any references.
In the language Rust, the type system has been extended to include also the variables lifetimes and resource acquisition is initialization. Unless one disables the features of the language, dangling pointers will be caught at compile time and reported as programming errors.
Some debuggers will automatically overwrite and destroy data that has been freed, usually with a specific pattern, such as 0xDEADBEEF (Microsoft's Visual C/C++ debugger, for example, uses 0xCC, 0xCD or 0xDD depending on what has been freed Visual C++ 6.0 memory-fill patterns). This usually prevents the data from being reused by making it useless and also very prominent (the pattern serves to show the programmer that the memory has already been freed).
Tools such as Polyspace, TotalView, Valgrind, Mudflap, Mudflap Pointer Debugging AddressSanitizer, or tools based on LLVMDhurjati, D. and Adve, V. Efficiently Detecting All Dangling Pointer Uses in Production Servers can also be used to detect uses of dangling pointers.
Other tools ( SoftBound, Insure++, and CheckPointer) instrument the source code to collect and track legitimate values for pointers ("metadata") and check each pointer access against the metadata for validity.
Another strategy, when suspecting a small set of classes, is to temporarily make all their member functions Virtual method: after the class instance has been destructed/freed, its pointer to the Virtual Method Table is set to NULL, and any call to a member function will crash the program and it will show the guilty code in the debugger.
The ARM64 memory tagging extension (MTE) - disabled by default on Linux systems, but can be enabled on Android 16 - triggers a segmentation fault when it detects use-after-free and buffer overflow.
|
|